package io.gitee.thant.utils;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class DataUtil {
	public static <T extends Object> T findRepeat(T[] dat) {
		if (null == dat) return null;
		
		Set<T> st = new HashSet<T>();
		for(int i=0; i<dat.length; ++i) {
			if (st.size()<i) {
				return dat[i-1];
			}
			st.add(dat[i]);
		}
		return null;
	}

	private static String mapkey(Map<String, Object> map, String[] keycolA) {
		StringBuilder sb = new StringBuilder(); 
		for (int i=0; i<keycolA.length; ++i) {
			sb.append(':').append(String.valueOf(map.get(keycolA[i])));
		}
		return sb.length()>0 ? sb.substring(1) : "";
	}
	
	public static long getLongValue(Object v) {
		if (v instanceof Integer) {
			return (int)v;
		} else if (v instanceof Long) {
			return (long)v;
		} else if (v instanceof Float) {
			return (long)(float)v;
		} else if (v instanceof Double) {
			return (long)(double)v;
		} else if (v instanceof Short) {
			return (short)v;
		} else if (v instanceof Byte) {
			return (byte)v;
		} else if (v instanceof Character) {
			return (char)v;
		} else if (v instanceof Boolean) {
			return (boolean)v ? 1 : 0;
		} else {
			try { return (long)(double)Double.valueOf(String.valueOf(v)); }
			catch (Exception e) { return 0; }
		}
	}

	public static double getFloatValue(Object v) {
		if (v instanceof Integer) {
			return (int)v;
		} else if (v instanceof Long) {
			return (long)v;
		} else if (v instanceof Float) {
			return (float)v;
		} else if (v instanceof Double) {
			return (double)v;
		} else if (v instanceof Short) {
			return (short)v;
		} else if (v instanceof Byte) {
			return (byte)v;
		} else if (v instanceof Character) {
			return (char)v;
		} else if (v instanceof Boolean) {
			return (boolean)v ? 1 : 0;
		} else {
			try { return (double)Double.valueOf(String.valueOf(v)); }
			catch (Exception e) { return 0; }
		}
	}

	private static String concat(Object val1, Object val2, String ch) {
		return String.valueOf(val1)+ch+String.valueOf(val2);
	}

	@SuppressWarnings("unchecked")
	public static Map<String, Object> row2col(List<Map<String, Object>> rows, String[] keycolA, String[] ruleA) {
		Map<String, Object> map = new HashMap<String, Object>();
		for(int i=0; i<rows.size(); ++i) {
			Map<String, Object> row = rows.get(i);
			String keyval = mapkey(row, keycolA);
			if (null == ruleA || ruleA.length<=0) {
				map.put(keyval, row);
			} else {
				Map<String, Object> col = (Map<String, Object>)map.get(keyval);
				if (null == col) {
					for (int j=0; j<ruleA.length; ++j) {
						String[] ruledef = StringUtil.split(ruleA[j], ":");
						String rulecol = ruledef[0];
						String ruletype = ruledef.length>1 ? ruledef[1] : "replace";
						if ("count".equalsIgnoreCase(ruletype)) {
							row.put(rulecol, new Long(1));
						}
					}
				} else {
					for (int j=0; j<ruleA.length; ++j) {
						String[] ruledef = StringUtil.split(ruleA[j], ":");
						String rulecol = ruledef[0];
						String ruletype = ruledef.length>1 ? ruledef[1] : "replace";
						Object colval = col.get(rulecol);
						switch(ruletype.toLowerCase()) {
						case "sum":
							row.put(rulecol, getLongValue(colval)+getLongValue(row.get(rulecol)));
							break;
						case "sumf":
							row.put(rulecol, getFloatValue(colval)+getFloatValue(row.get(rulecol)));
							break;
						case "concat":
							String split = ruledef.length>2 ? StringUtil.URLDecode(ruledef[2], "UTF-8") : ",";
							row.put(rulecol, concat(colval, row.get(rulecol), split));
							break;
						case "count":
							row.put(rulecol, (Long)col.get(rulecol)+1);
							break;
						default: //replace
						}
					}
				}
				map.put(keyval, row);
			}
		}
		return map;
	}
	
	public static Object deepCopy(Object sour) {
		if (null == sour) return null;
		return SerializeUtil.readObject(SerializeUtil.writeObject(sour));
	}

	public static List<Map<String, Object>> group(List<Map<String, Object>> rows, String[] keycolA, String[] ruleA) {
		for(int i=0; i<rows.size(); ++i) {
			Map<String, Object> row = rows.get(i);
			String keyval = mapkey(row, keycolA);
			row.put("#key", keyval);
		}
		
		rows.sort(new Comparator<Map<String, Object>>() {
			public int compare(Map<String, Object> o1, Map<String, Object> o2) {
				return o1.get("#key").toString().compareTo(o2.get("#key").toString());
			}
		});

		int cnt = 1;
		Map<String, Object> currow = rows.get(rows.size()-1);
		for(int i=rows.size()-1; i>=0; --i) {
			Map<String, Object> prvrow = i>0 ? rows.get(i-1) : null;
			if (prvrow != null && 0==currow.get("#key").toString().compareTo(prvrow.get("#key").toString())) {
				cnt++;
				rows.remove(i);
				for (int j=0; j<ruleA.length; ++j) {
					String[] ruledef = StringUtil.split(ruleA[j], ":");
					String rulecol = ruledef[0];
					String ruletype = ruledef.length>1 ? ruledef[1] : "count";
					Object colval = currow.get(rulecol);
					switch(ruletype.toLowerCase()) {
					case "sum":
						prvrow.put(rulecol, getLongValue(colval)+getLongValue(prvrow.get(rulecol)));
						break;
					case "sumf":
						prvrow.put(rulecol, getFloatValue(colval)+getFloatValue(prvrow.get(rulecol)));
						break;
					case "concat":
						String split = ruledef.length>2 ? StringUtil.URLDecode(ruledef[2], "UTF-8") : ",";
						prvrow.put(rulecol, concat(prvrow.get(rulecol), colval, split));
						break;
					}
				}
			} else {
				for (int j=0; j<ruleA.length; ++j) {
					String[] ruledef = StringUtil.split(ruleA[j], ":");
					String rulecol = ruledef[0];
					String ruletype = ruledef.length>1 ? ruledef[1] : "count";
					switch(ruletype.toLowerCase()) {
					case "count":
						currow.put(rulecol, cnt);
						break;
					}
				}
				cnt = 1;
			}
			currow = prvrow;
		}
		return rows;
	}
}
